﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Reflection;

namespace TerenXNA
{
    public class Terrain
    {
        public static int watki = 0; 
        // czy i jaki teren wczytany
        // 0 = nie wczytany; 1,2,3... level of detail
        public int loaded = 0;
        public bool lloaded = false;
        // LoD do wczytania, potrzebny tu do przekaznia do wątku wczytującego teren
        private int tLoD = -1;
        // wspolrzedne pola x,y
        private int wspx;
        private int wspy;
        // zmienne jakies do dzialajacego lod
        private int ddd = 1;
        // dane terenu
        HeightData hd;
        HeightData ld; 

        //utworzenie obiektu terenu
        public Terrain(int x, int y)
        {
            this.wspx = x;
            this.wspy = y;
            ld = new HeightData(World.tileTterrainLoadLdTextures, "tiles_ld/tile_" + wspx + "_" + wspy, "texture_ld/tile_" + x + "_" + y + ".png", 33, 33, 32);
            if (!ld.loadTerrain()) return;
            ld.load();
            lloaded = true;
        }

        //wygenerowanie terenu o odpowiednim level of detail
        public void genTerrain()
        {
            Thread.Sleep(wspx * wspy * 2);
            if (hd == null) hd = new HeightData(World.tileTterrainLoadHdTextures, "tiles_hd/tile_" + wspx + "_" + wspy, "texture_ld/tile_" + wspx + "_" + wspy + ".png", 129, 129, 8);
            if (!hd.loadTerrain()) return;
            hd.load();
            loaded = tLoD;
            tLoD = -1;
            watki--;
        }

        //usuniecie terenu z pameici
        public void unloadTerrain()
        {
            hd = null;
            loaded = 0;
        }

        //render terenu
        public void draw(int lod)
        {
            // ustawienia grafiki 
            RasterizerState rs = new RasterizerState();
            rs.CullMode = CullMode.CullClockwiseFace;

            if (World.isGridVisible) 
                rs.FillMode = FillMode.WireFrame;
            else rs.FillMode = FillMode.Solid;
            Game1.device.RasterizerState = rs;
            //Game1.effect.CurrentTechnique = Game1.effect.Techniques["ColoredNoShading"];

            if (ld.textureLoaded && World.isTextureVisible)
            {
                Game1.effect.Parameters["xTexture"].SetValue(ld.imageTexture);
                Game1.effect.CurrentTechnique = Game1.effect.Techniques["Textured"];
            }
            else
            {
                Game1.effect.CurrentTechnique = Game1.effect.Techniques["Colored"];
            }
            //zastosowanie ustawien
            foreach (EffectPass pass in Game1.effect.CurrentTechnique.Passes)
                    pass.Apply();

            // kontrola lod 
            if (lod > -1) ddd = 1;
            if (lod > 4 && tLoD == -1) {
                if (lloaded)
                {
                    Game1.device.Indices = ld.myIndexBuffer;
                    Game1.device.SetVertexBuffer(ld.myVertexBuffer);
                    Game1.device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, ld.verticesLength, 0, ld.indicesLength / 3);
                }
                unloadTerrain(); 
                return; 
            }

            if (loaded != ddd)
            {
                if (lloaded)
                {
                    Game1.device.Indices = ld.myIndexBuffer;
                    Game1.device.SetVertexBuffer(ld.myVertexBuffer);
                    Game1.device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, ld.verticesLength, 0, ld.indicesLength / 3);
                }
                if (tLoD == -1&&watki<20)
                {
                    watki++;
                    tLoD = ddd;
                    //genTerrain();
                    Thread gThread = new Thread(new ThreadStart(genTerrain));
                    gThread.Start();
                }
            }
            else
            {
                    Game1.device.Indices = hd.myIndexBuffer;
                    Game1.device.SetVertexBuffer(hd.myVertexBuffer);
                    Game1.device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, hd.verticesLength, 0, hd.indicesLength / 3);
            }
        }

        // metoda zwracająca wysokość wskazanego wierzchołka
        public float getHeight(int x, int y)
        {
            if (hd == null) return 0;
            if (hd.heightData == null) return 0;
            return  hd.heightData[x/8, y/8];
        }
    }
}
